<h2>Links</h2>
<a href="http://localhost/mysql/">localhost/mysql</a>


<h2>Instructions</h2>
<p>To get this app to run on your machine edit your <tt>database.yml</tt> file then run:</p>

<pre>
  rake db:create
  rake db:migrate
  rake db:dataset:load_fixtures DATASET=nasa
</pre>

<p>Then go to the depenses page and click "Edit".  Try every combination of adding, updating and deleting, watch the params and the validations and check your database to see when things get committed.</p>

<h2>The Goal</h2>

<p>
  The goal of this app is to show how to create a complex form that captures 3 kinds of associations using skinny controllers and fat models.  The 3 kinds of associations it demos are:
</p>

<ul>
  <li>has_many</li>
  <li>has_many :through (with extra attributes on the join table)</li>
  <li>has_many :through (with checkboxes to select the associated models)</li>
</ul>

<h2>Technical Requirements</h2>

<p>
  A form that manages a model and its related model must:
</p>

<ul>
  <li>Handle validation of the main model as well as all of the associated models</li>
  <li>Create fully valid XHTML</li>
  <li>
    Have completely atomic saves:
    <ul>
      <li>If you add a child item and there is a form error the child item needs to be redisplayed to the user, but not saved to the database</li>
      <li>If you update a child item and there is an error the child item must be correctly displayed to the user but not saved in the database</li>
      <li>If you remove a child item and there is a form error, the child must be hidden from view on the page but remain in the database</li>
      <li>On a successful save all new objects must be created, existing objects updated and removed object deleted</li>
    </ul>
  </li>
  <li>Run updates in a transaction</li>
  <li>
    Handle both means of data entry:
    <ul>
      <li>As subforms that can be added</li>
      <li>As a list of checkboxes that can be checked</li>
    </ul>
  </li>
  <li>Handle all of the rails helpers such as check boxes, date fields and radio buttons</li>
  <li>Work the same every time a user clicks refresh or submits</li>
</ul>

<h2>Domain Description</h2>

<p><strong>Depense</strong> is the main object here, and is defined as:</p>

<pre>
  class Depense &lt; ActiveRecord::Base
    has_many :tasks, :single_save =&gt; true
    has_many :plinks, :single_save =&gt; true
    has_many :parcelles, :through =&gt; :plinks
    has_many :categorizations
    has_many :categories, :through =&gt; :categorizations, :single_save =&gt; true
    has_one :depense_user, :single_save =&gt; true
    has_and_belongs_to_many :tags

    validates_presence_of :name
    validates_associated :tasks, :plinks
  end
</pre>

<p><strong>Task</strong> belongs to <strong>Depense</strong> and requires a name:</p>

<pre>
  class Task &lt; ActiveRecord::Base
    belongs_to :depense
    validates_presence_of :name
  end
</pre>

<p><strong>Parcelle</strong> is joined to <strong>Depense</strong> through an <strong>Plink</strong> and requires both an parcelle and a title:</p>

<pre>
  class Parcelle &lt; ActiveRecord::Base
    has_many :plinks
    has_many :depenses, :through => :plinks
  end
</pre>

<pre>
  class Plink &lt; ActiveRecord::Base
    belongs_to :depense
    belongs_to :parcelle
    validates_presence_of :title
    validates_presence_of :parcelle_id
  end
</pre>

<p>The depense-parcelle-plink part of the domain requires additional fields on the join table.</p>

<p><strong>Category</strong> is joined to <strong>Depense</strong> through a <strong>Categorization</strong>:</p>

<pre>
  class Category &lt; ActiveRecord::Base
    has_many :categorizations
    has_many :depenses, :through => :categorizations
    validates_presence_of :name
  end
</pre>

<pre>
  class Categorization &lt; ActiveRecord::Base
    belongs_to :depense
    belongs_to :category
  end
</pre>

<p>The depense-categorization-category part of the domain is very similar to a <tt>has_and_belongs_to_many</tt> relationship.</p>

<h3>Handling validation</h3>

<p>Instead of using the rails default <tt>error_messages_for</tt> helper, just use a partial.</p>

<h3>Create fully valid XHMTL</h3>

<p>To output valid XHTML you must:</p>

<ul>
  <li>Set an index on every child field</li>
  <li>Have the javascript that dynamically adds fields set the index correctly as well</li>
</ul>

<p>This requires a complex interaction between the partial and the javascript code.  The necessary parts are spread across multiple files:</p>

<ul>
  <li>
    public/javascripts/application.js contains a generic class that you can use for any subform, and add multiple subforms to a page:
    <pre>
  var Subform = Class.create({
    lineIndex: 1,
    parentElement: "",

    // rawHTML contains the html to add using the "add" link
    // lineIndex should be the length of the original array
    // parentElement is the id of the div that the subforms attach to
    initialize: function(rawHTML, lineIndex, parentElement) {
      this.rawHTML        = rawHTML;
      this.lineIndex      = lineIndex;
      this.parentElement  = parentElement;
    },

    // parses the rawHTML and replaces all instances of the word
    // INDEX with the line index
    // So the HTML on that rails outputs will be INDEX, but when this
    // is added to the dom it has the correct id
    parsedHTML: function() {
      return this.rawHTML.replace(/INDEX/g, this.lineIndex++);
    },

    // handles the inserting of the child form
    add: function() {
      new Insertion.Bottom($(this.parentElement), this.parsedHTML());
    }
  });
    </pre>
  </li>
  <li>
    Each child form's partial must instantiate the javascript - I use a content_for block and inject that code into the head of the document
    <pre>
      &lt;%- content_for :head do -%&gt;
        &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
         //&lt;![CDATA[
           taskForm = new Subform(&#x27;&lt;%= escape_javascript(render(:partial =&gt; &quot;task&quot;, :object =&gt; Task.new, :locals =&gt; {:index =&gt; &quot;INDEX&quot;})) %&gt;&#x27;,&lt;%= @depense.tasks.length %&gt;,&#x27;tasks&#x27;);
           plinkForm = new Subform(&#x27;&lt;%= escape_javascript(render(:partial =&gt; &quot;plink&quot;, :object =&gt; Plink.new, :locals =&gt; {:index =&gt; &quot;INDEX&quot;})) %&gt;&#x27;,&lt;%= @depense.plinks.length %&gt;,&#x27;plinks&#x27;);
         //]]&gt;
        &lt;/script&gt;
      &lt;%- end -%&gt;
    </pre>
  </li>
  <li>If you are using content_for like I am, then the app/views/application.html.erb file must have a corresponding yield statement</li>
</ul>

<p>Rails makes setting the index properly a breeze with it's <tt>:index</tt> option and the <tt>partial_counter</tt> local variable in partials that are fed collections.</p>

<p>The index does not work with radio buttons (this may be fixed in edge) and the standard rails labels output invalid XHTML, so those have to be coded manually.</p>

<h3>Marking objects for deletion</h3>

<p>This means being able to mark an object for deletion before actually deleting it.  I've accomplished this in a site-wide way with <tt>config/initializers/marked_for_deletion.rb</tt></p>

<h3>Performing the save in a transaction</h3>

<p>Rails doesn't include the after_update callbacks in it's default transactions, so you have to do that yourself.  It has 3 parts:</p>

<ul>
  <li>
    A method in the depense model that performs the save in a transaction and re-raises the error:
    <pre>
  class &lt;&lt; self
    def save(depense,depense_attributes = nil)
      depense.attributes = depense_attributes unless depense_attributes.nil?
      updated = false
      Depense.transaction do
        updated = depense.save
      end
      updated
    rescue Exception =&gt; e
      throw e
    end
  end
    </pre>
  </li>
  <li>
    Update the controller code to call the correct method:
    <pre>
  def create
    @depense = Depense.new(params[:depense])
    respond_to do |format|
      if Depense.save(@depense)
        ...
      else
        ...
      end
    end
  end      
    </pre>
    <br/>
    <pre>
  def update
    @depense = Depense.find(params[:id])
    params[:depense][:existing_task_attributes] ||= {} 

    respond_to do |format|
      if Depense.save(@depense,params[:depense])
        ...
      else
        ...
      end
    end
  end
    </pre>
  </li>
</ul>